package org.luosl.webmagicx.handler

import java.io.{File, IOException}

import org.apache.commons.codec.digest.DigestUtils
import org.apache.commons.io.{FileUtils, FilenameUtils}
import org.luosl.webmagicx.conf.{ConfException, XmlProps, SpiderConf}
import us.codecraft.webmagic.{ResultItems, Task}
import org.luosl.webmagicx.conf.MatcherConverters._
import org.luosl.webmagicx.conf.PropType._

import scalaj.http.{Http, HttpResponse}

class DownloadHandler(sc:SpiderConf, task:Task, props:XmlProps) extends AbstractHandler (sc, task, props){

  private val needProcessCell:List[XmlProps] = props.props("*")
  private val downloadCells:List[DownloadProps] = needProcessCell.map{ prop=>
    DownloadProps(
      prop.value("src")(strType),
      prop.value("target")(strType),
      prop.value("saveDir")(strType),
      prop.valueOrDefault("returnValue")(strType)("fileName"),
      prop.valueOrDefault("retryTimes")(intType)(0),
      prop.valueOrDefault("must")(booleanType)(true)
    )
  }

  override def processHandle(items: ResultItems, task: Task): Unit = {
    downloadCells.foreach{ prop=>
      if(!items.isSkip){
        val needDownloadUrl:String = items.get(prop.src)
        val result:String = try{
          val byteArr:Array[Byte] = download(needDownloadUrl,prop.retryTimes)
          logInfo(s"文件[url=$needDownloadUrl]下载成功!!")
          prop.returnValue match {
            case "md5" =>
              val md5:String = DigestUtils.md5Hex(byteArr)
              save(byteArr, s"${prop.saveDir}${File.separator}$md5")
              md5
            case str:String if str == "path" || str == "fileName" =>
              val fileName:String = FilenameUtils.getName(needDownloadUrl)
              val savePath:String = s"${prop.saveDir}${File.separator}$fileName"
              save(byteArr,savePath)
              if(str == "path") savePath else fileName
            case str:String =>
              throw ConfException(s"DownloadHandler download 的 returnValue 属性不支持:$str")
          }
        }catch {
          case _:Exception => null
        }
        if(null == result && prop.must){
          items.setSkip(true)
          logInfo(s"DownloadHandler: 下载[downloadUrl=$needDownloadUrl, must=${prop.must}]失败，[url=${items.get("_url")}]被跳过...")
        }
        items.put(prop.target, result)
      }
    }
  }

  /**
    * 下载
    * @param url url
    * @param retryTimes retryTimes
    * @return
    */
  def download(url:String,retryTimes:Int):Array[Byte] = {
    def downloadAsByteArr(url:String):Array[Byte] = Http(url).asBytes.body
    try{
      downloadAsByteArr(url)
    }catch {
      case e:IOException =>
        val nowTimes:Int = retryTimes - 1
        if(nowTimes > 0){
          logInfo(s"正在进重新尝试下载...")
          download(url, retryTimes - 1)
        }else{
          throw new RuntimeException(s"无法下载:url=$url", e)
        }
    }
  }

  /**
    * 保存文件
    * @param byteArr byteArr
    * @param path path
    */
  def save(byteArr:Array[Byte],path:String): Unit ={
    val file:File = new File(path)
    val parent:File = file.getParentFile
    if(parent.exists() && parent.isFile)
      throw new RuntimeException(s"${parent.getAbsolutePath}不是一个目录")
    if(!parent.exists())
      FileUtils.forceMkdir(parent)
    FileUtils.writeByteArrayToFile(file,byteArr)
  }
}

case class DownloadProps(src:String, target:String, saveDir:String, returnValue:String, retryTimes:Int, must:Boolean)
